package org.jivesoftware.openfire.plugin;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.log4j.BasicConfigurator;
import org.jivesoftware.database.SequenceManager;
import org.jivesoftware.openfire.PresenceManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.openfire.event.SessionEventListener;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.plugin.chatlogs.DbChatLogsManager;
import org.jivesoftware.openfire.plugin.chatlogs.HttpUtils;
import org.jivesoftware.openfire.plugin.kafka.ChatLogsProducer;
import org.jivesoftware.openfire.plugin.kafka.KafkaUtil;
import org.jivesoftware.openfire.plugin.listener.SessionEventListenerImpl;
import org.jivesoftware.openfire.plugin.model.ChatLogs;
import org.jivesoftware.openfire.plugin.model.JsonUtils;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.*;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @description 聊天记录拦截插件
 */
public class YouXinChatLogsPlugin implements PacketInterceptor, Plugin {

    private static final Logger log = LoggerFactory.getLogger(YouXinChatLogsPlugin.class);

    private static PluginManager pluginManager;

    private static DbChatLogsManager dbChatLogsManager;

    private static UserManager userManager;

    private static PresenceManager presenceManager;

    private InterceptorManager interceptorManager;

    private SessionEventListener sessionEventListener;

    public YouXinChatLogsPlugin() {
        interceptorManager = InterceptorManager.getInstance();
        dbChatLogsManager = DbChatLogsManager.getInstance();

        sessionEventListener = new SessionEventListenerImpl();
    }

    @Override
    public void initializePlugin(PluginManager manager, File pluginDirectory) {
        //TODO 自动快速地使用缺省Log4j环境,提交插件时需要注释/删除此行代码
//        BasicConfigurator.configure();

        interceptorManager.addInterceptor(this);
        pluginManager = manager;

        userManager = XMPPServer.getInstance().getUserManager();
        presenceManager = XMPPServer.getInstance().getPresenceManager();

        SessionEventDispatcher.addListener(sessionEventListener);
        log.info("@@@@@@@@@@ YouXinChatLogsPlugin init");
    }

    @Override
    public void destroyPlugin() {
        log.info("@@@@@@@@@@ YouXinChatLogsPlugin destroy");
        interceptorManager.removeInterceptor(this);
        SessionEventDispatcher.removeListener(sessionEventListener);
    }

    @Override
    public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException {
        /*if (session != null) {
            debug(packet, incoming, processed, session);
        }*/
        // 程序执行中 || 是否为结束或返回状态（是否是当前session用户发送消息）
        if (processed || !incoming) {
            return;
        }
        this.doAction(packet, incoming, processed, session);
    }

    /**
     * <b>function:</b> 执行保存/分析聊天记录动作
     *
     * @param packet   数据包
     * @param incoming true表示发送方
     * @param session  当前用户session
     */
    private void doAction(Packet packet, boolean incoming, boolean processed, Session session) throws PacketRejectedException {
        Packet copyPacket = packet.createCopy();
        if (packet instanceof Message) {
            Message message = (Message) copyPacket;
            // 消息接收用户
            JID recipient = copyPacket.getTo();

            //TODO 拦截消息不允许发送,后期会用到
            /*if(recipient.getNode().equals("056e0438ac38dc4ddc2bd7f439c9e778")){
                throw new PacketRejectedException("不讓你發消息！");
            }*/

            // 消息接受用户不为空
            if (recipient != null) {
                // if broadcast message or user is not exist
                if (null == recipient.getNode() || (message.getType() == Message.Type.chat && !UserManager.getInstance().isRegisteredUser(recipient))) {
                    return;
                }
                // not from the same domain
                /*else if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) {
                    return;
                }*/
                // 客户端标识为空
                else if ("".equals(recipient.getResource())) {
                    return;
                }
            }
            // 消息不为null时
            if (message.getBody() != null) {
                // 单聊
                if (message.getType() == Message.Type.chat) {
                    log.info("@@@@@@@@@@ 单人聊天信息：{}", message.toXML());

                    //推送聊天信息
                    Map<String, String> data = new HashMap<>();
                    data.put("fromUser", message.getFrom().getNode());
                    data.put("toUser", message.getTo().getNode());
                    data.put("content", message.getBody());
                    //调用社区接口
                    String result = HttpUtils.send(HttpUtils.PUSH_URL,JSON.toJSONString(data));
                    log.info("@@@@@@@@@@ 消息推送内容:{}，返回结果：{} " ,JSON.toJSONString(data),result);
                    if ("true".equals(result)) {
                        log.info("@@@@@@@@@@ 消息推送成功:{} " ,JSON.toJSONString(data));
                    }else {
                        log.info("@@@@@@@@@@ 消息推送失败! " );
                    }

                    // 插入队列
                    ChatLogs logs = this.get(packet, incoming, session, 0);
                    ChatLogsProducer.send(KafkaUtil.KAFKA_TOPIC_VALUE, JSONObject.toJSONString(logs));
                    try {
                        // 获取消息接收用户状态
                        Presence status = presenceManager.getPresence(userManager.getUser(recipient.getNode()));
                        if (status == null) {
                            // 消息接收用户是否在线
                            boolean isOnline = XMPPServer.getInstance().getSessionManager().getActiveSessionCount(message.getTo().getNode()) > 0;
                            //TODO 消息接收用户不在线，实现处理离线推送逻辑
                            if (!isOnline) {

                                // pusgMsg("xxxxx", data);

                            }
                        }
                    } catch (UserNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                // 群聊
                else if (message.getType() == Message.Type.groupchat) {

                    Map<String,Object> map = JsonUtils.fromJson(message.getBody(),Map.class);
                    int type = (Integer) map.get("type");

                    //type = 8 或者 type 在300-400之间不要推送
                    if (!(type == 8 || (type >= 300 && type <= 400))){
                        log.info("@@@@@@@@@@ 群聊天信息：{}", message.toXML());

                        //推送聊天信息
                        Map<String, String> data = new HashMap<>();
                        data.put("fromUser", message.getFrom().getNode());
                        data.put("toUser", message.getTo().getNode());
                        data.put("content", message.getBody());

                        //调用社区接口
                        String result = HttpUtils.send(HttpUtils.PUSH_GROUP_URL,JSON.toJSONString(data));
                        log.info("@@@@@@@@@@ 群消息推送内容:{}，返回结果：{} " ,JSON.toJSONString(data),result);
                        if ("true".equals(result)) {
                            log.info("@@@@@@@@@@ 群消息推送成功:{} " ,JSON.toJSONString(data));
                        }else {
                            log.info("@@@@@@@@@@ 群消息推送失败! " );
                        }

                        //存储消息记录
                        ChatLogsProducer.send(KafkaUtil.KAFKA_TOPIC_VALUE, JSONObject.toJSONString(this.get(packet, incoming, session, 1)));
                    }

                    
                   /* List<?> els = message.getElement().elements("x");
                    if (els != null && !els.isEmpty()) {
                        log.info("@@@@@@@@@@ 群聊天信息：{}", message.toXML());
                        ChatLogsProducer.send(KafkaUtil.KAFKA_TOPIC_VALUE, JSONObject.toJSONString(this.get(packet, incoming, session, 1)));
                    } else {
                        log.info("@@@@@@@@@@ 群系统信息：{}", message.toXML());
                    }*/
                //其他信息
                } else {
                    log.info("@@@@@@@@@@ 其他信息：{}", message.toXML());
                }
            }
        }
        //TODO 用户的在线/离线状态已经迁移到SessionEventListenerImpl中
        /*else if (packet instanceof IQ) {
            IQ iq = (IQ) copyPacket;
            if (iq.getType() == IQ.Type.set && iq.getChildElement() != null && "bind".equals(iq.getChildElement().getName())) {
                log.info("@@@@@@@@@@ 用户登录成功：{}", session.getAddress().asBareJID());
            }
        } else if (packet instanceof Presence) {
            Presence presence = (Presence) copyPacket;
            if (presence.getType() == Presence.Type.unavailable) {
                log.info("@@@@@@@@@@ 用户退出服务器成功：{}", presence.toXML());
            }
        }*/
    }

    /**
     * <b>function:</b> 创建一个聊天记录实体对象，并设置相关数据
     *
     * @param packet   数据包
     * @param incoming 如果为ture就表明是发送者
     * @param session  当前用户session
     * @return 聊天实体
     */
    private ChatLogs get(Packet packet, boolean incoming, Session session, int type) {
        Message message = (Message) packet;
        ChatLogs logs = new ChatLogs();
        JID jid = session.getAddress();
        if (incoming) { // 发送者
            logs.setSender(jid.getNode());
            JID recipient = message.getTo();
            logs.setReceiver(recipient.getNode());
        }

        // 设置uuid为Message自带ID
        logs.setUuid(message.getID());

        logs.setContent(message.getBody());
        logs.setCreateDate(new Timestamp(System.currentTimeMillis()));
        logs.setDetail(message.toXML());
        logs.setLength(logs.getContent().length());
        logs.setState(0);
        logs.setType(type);
        logs.setSessionJid(jid.toString());
        // 生成主键id，利用序列生成器
        long messageID = SequenceManager.nextID(ChatLogs.ChatLogsConstants.CHAT_LOGS);
        logs.setMessageId(messageID);
        return logs;
    }

    /**
     * <b>function:</b> 调试信息
     *
     * @param packet    数据包
     * @param incoming  如果为ture就表明是发送者
     * @param processed 执行
     * @param session   当前用户session
     */
    private void debug(Packet packet, boolean incoming, boolean processed, Session session) {
        String info = "[ packetID: " + packet.getID() + ", to: " + packet.getTo() + ", from: " + packet.getFrom() + ", incoming: " + incoming + ", processed: " + processed + " ]";
        /*long timed = System.currentTimeMillis();
        debug("################### start ###################" + timed);
        debug("id:" + session.getStreamID() + ", address: " + session.getAddress());
        debug("info: " + info);
        debug("xml: " + packet.toXML());
        debug("################### end #####################" + timed);*/
        log.info("@@@@@@@@@@ id:" + session.getStreamID() + ", address: " + session.getAddress());
        log.info("@@@@@@@@@@ info: {}", info);
        log.info("@@@@@@@@@@ xml: " + packet.toXML());
    }

    private void debug(Object message) {
        if (true) {
            System.out.println(message);
        }
    }

    public void pusgMsg(String url, Map<String, Object> data) {
        try {
            URL realUrl = new URL(url);
            HttpURLConnection urlConn = (HttpURLConnection) realUrl.openConnection();
            urlConn.setDoOutput(true);
            urlConn.setDoInput(true);
            urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            urlConn.setUseCaches(false);
            urlConn.setInstanceFollowRedirects(true);
            if (null != data) {
                OutputStream out = urlConn.getOutputStream();
                out.write(getBytes(data));
                out.flush();
                out.close();
            }
            InputStream is = urlConn.getInputStream();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("@@@@@@@@@@ 离线消息推送成功: " + JSON.toJSONString(data));
    }

    private static byte[] getBytes(Map<String, Object> data) throws Exception {
        StringBuffer sb = new StringBuffer();
        for (String key : data.keySet()) {
            sb.append(key).append('=').append(data.get(key)).append('&');
        }
        return sb.substring(0, sb.length() - 1).toString().getBytes("UTF-8");
    }

}
